#include "matrix.h"


//
// Created by hay boy on 2022/6/24.
//
template<class T>
matrix<T>::matrix(int theRows, int theColumns) {
    if(theRows<0||theColumns<0){
        throw illegalParameterValue("Rows and columns must be > 0.");
    }
    if((theRows==0||theColumns==0)&&(theRows!=0||theColumns!=0)){
        throw illegalParameterValue("Either both or neither rows and columns should be zero.");
    }
    this->theRows = theRows;
    this->theColumns = theColumns;
    element = new T[theRows*theColumns];
}
template<class Y>
istream &operator>>(istream & in, const matrix<Y> & m) {
    Y y;
    for(int i = 0;i<m.theRows*m.theColumns;i++){
        cin>>y;
        m.element[i] = y;
    }
    return in;
}
template<class Y>
ostream &operator<<(ostream & out, const matrix<Y> & m) {
    int k = 0;  // index into element array
    for (int i = 0; i < m.theRows; i++)
    {// do row i
        for (int j = 0; j < m.theColumns; j++)
            out << m.element[k++] << "  ";

        // row i finished
        out << endl;
    }

    return out;
}

template<class T>
matrix<T>::matrix(const matrix<T> & m) {
    theRows = m.theRows;
    theColumns = m.theColumns;
    element = new T[theRows*theColumns];
    copy(m.element,m.element+theRows*theColumns,element);
}

template<class T>
T &matrix<T>::operator()(int i, int j) const {
   if(i<1||i>theRows||j<1||j>theColumns){
       throw matrixIndexOutOfBounds();
   }
   return element[(i-1)*theColumns+j-1];
}

template<class T>
matrix<T> &matrix<T>::operator=(const matrix<T> & m) {
    if(this!=&m){
        delete []element;
        theRows = m.theRows;
        theColumns = m.theColumns;
        element = new T[theRows*theColumns];
        copy(m.element,m.element+theRows*theColumns,element);
    }
    return *this;
}

template<class T>
matrix<T> matrix<T>::operator+() const {
    return *this;
}

template<class T>
matrix<T> matrix<T>::operator+(const matrix<T> &m) const {
    if(theRows!=m.theRows||theColumns!=m.theColumns){
        throw matrixSizeMismatch();
    }
    matrix<T> w(theRows,theColumns);
    for(int i = 0;i<theRows*theColumns;i++){
        w.element[i] = element[i]+m.element[i];
    }
    return w;
}

template<class T>
matrix<T> matrix<T>::operator-() const {
    matrix<T> w(theRows,theColumns);
    for(int i = 0;i<theRows*theColumns;i++){
        w.element[i] = -element[i];
    }
    return w;
}

template<class T>
matrix<T> matrix<T>::operator-(const matrix<T> &m) const {
    if(theRows!=m.theRows||theColumns!=m.theColumns){
        throw matrixSizeMismatch();
    }
    matrix<T> w(theRows,theColumns);
    for(int i = 0;i<theRows*theColumns;i++){
        w.element[i] = element[i]-m.element[i];
    }
    return w;
}

template<class T>
matrix<T> matrix<T>::operator*(const matrix<T> &m) const {
    if(theColumns!=m.theRows){
        throw matrixSizeMismatch();
    }
    matrix<T>w(theRows,m.theColumns);
    int ct = 0,cm = 0,cw = 0;
    for(int i = 1;i<=theRows;i++){
        for(int j = 1;j<=m.theColumns;j++){
            T sum = element[ct]*m.element[cm];
            for(int k = 2;k<=theColumns;k++){
                ct++;
                cm += m.theColumns;
                sum += element[ct]*m.element[cm];
            }
            w.element[cw++] = sum;
            ct -= theColumns-1;
            cm = j;
        }
        ct+=theColumns;
        cm = 0;
    }
    return w;
}

template<class T>
matrix<T> &matrix<T>::operator+=(const T &x) {
    for (int i = 0; i < theRows * theColumns; i++)
        element[i] += x;
    return *this;
}

template<class T>
matrix<T> &matrix<T>::operator-=(const T &x) {
    for (int i = 0; i < theRows * theColumns; i++)
        element[i] -= x;
    return *this;
}

template<class T>
matrix<T> &matrix<T>::operator*=(const T &x) {
    for (int i = 0; i < theRows * theColumns; i++)
        element[i] *= x;
    return *this;
}

template<class T>
matrix<T> &matrix<T>::operator/=(const T &x) {
    for (int i = 0; i < theRows * theColumns; i++){
        if(element[i]!=0){
            element[i]/=x;
        }
    }
    return *this;
}

template<class T>
matrix<T> matrix<T>::tranpose() {
    matrix<T> w(this->theColumns,this->theRows);
    for(int i = 0;i<w.theRows;i++){
        for(int j = 0;j<w.theColumns;j++){
            w(i+1,j+1) = element[j*theColumns+i];
        }
    }
    return w;
}
